package com.fintech.third.service.sms;

import java.net.URLEncoder;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.fintech.common.enums.InterfaceMethodEnum;
import com.fintech.common.enums.StateEnum;
import com.fintech.common.util.PropertiesUtils;
import com.fintech.common.util.RedisUtils;
import com.fintech.third.constant.Constants;
import com.fintech.third.constant.DataRow;
import com.fintech.third.constant.ResponseResult;
import com.fintech.third.constant.SmsTypeEnum;
import com.fintech.third.dao.sms.SmsLogMapper;
import com.fintech.third.dao.sms.SmsTemplateNewMapper;
import com.fintech.third.model.message.MicroNetResponse;
import com.fintech.third.model.reqlog.IntDataQueryLog;
import com.fintech.third.model.sms.SmsLog;
import com.fintech.third.model.sms.SmsParameter;
import com.fintech.third.model.sms.SmsTemplateNew;
import com.fintech.third.service.reqlog.IntDataQueryLogService;
import com.fintech.third.utils.HttpUtil;
import com.fintech.third.utils.ThirdSystemUtil;
import com.marvels.common.utils.MarvelsLogUtil;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20190711.SmsClient;
import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20190711.models.SendStatus;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
/**
 * 短信
 * @author lvgb
 *
 */

@Service("smsService")
public class SmsServiceImpl implements SmsService {

	@Autowired
	private SmsTemplateNewMapper smsTemplateMapper;

	@Autowired
	private SmsLogMapper smsLogMapper;

	@Value("${tencent.sms.appid}")
	private String appid;

	@Value("${tencent.sms.smsSign}")
	private String smsSign;

	@Value("${tencentcloud.secretId}")
	private String secretId;

	@Value("${tencentcloud.secretKey}")
	private String secretKey;

	@Value("${tencentcloud.region}")
	private String region;

	@Autowired
	private IntDataQueryLogService intDataQueryLogService;

	/**
	 * 验证验证码
	 * @param mobileNo 手机号
	 * @param smsCode  验证码
	 * @param type     发送类型
	 * @return
	 */
	@Override
	public boolean checkCode(String mobileNo, String smsCode, String type){
		String timeoutStr = PropertiesUtils.getString("sms.codetimeout");
		int timeoutInt = StrUtil.isEmpty(timeoutStr) ? 900 : Integer.valueOf(timeoutStr);//验证码失效时间默认为900秒，可在配置文件中设置
		String redisCodeKey = type + mobileNo + "code";
		String redisNumKey = type + mobileNo + "num";
		// 兼容整合后因为redis反序列化问题
		Object codeRedis = null;
		try {
			codeRedis = RedisUtils.getInstance().get(redisCodeKey);
		} catch (Exception e) {
			RedisUtils.getInstance().del(redisCodeKey);
		}
		if(codeRedis == null){
			return false;
		}
		String code_save = codeRedis.toString().split("\\|")[0];
		long time  = Long.valueOf(codeRedis.toString().split("\\|")[1]);
		if((System.currentTimeMillis() - time) / 1000 > timeoutInt){
			return false;
		}

		String isUat = PropertiesUtils.getString("sms.isuat");
		if("isuat".equals(isUat)){
			return true;
		}

		if(!smsCode.equals(code_save)){
			return false;
		}

		//删除redis验证码和计数
		RedisUtils.getInstance().del(redisCodeKey);
		RedisUtils.getInstance().del(redisNumKey);
		return true;
	}

	/**
	 * 发送短信
	 *
	 * @param mobileNo
	 *            ==》 手机号
	 * @param smsType
	 *            ==》 发送类型
	 * @return
	 */
	@Override
	public ResponseResult sendSms(String mobileNo, SmsParameter smsParameter, SmsTypeEnum smsType) {
		try {
			if("registerMobileKey".equals(smsType.getTemplateName())){//短信验证码
				return sendAuthenticationCode(mobileNo,smsType);
			}else {
				sendCodeMes(mobileNo,smsParameter,smsType.getTemplateName());
				return ResponseResult.getSuccessResult("发送成功", null);
			}
		} catch (Exception e) {
			MarvelsLogUtil.getInstance().error("发送短信出现异常", e);
			return ResponseResult.getExceptionResult("发送异常");
		}
	}
	/**
	 * 发送短信验证码
	 * @param mobileNo
	 * @param smsParameter
	 * @param smsType
	 */
	private ResponseResult sendAuthenticationCode(String mobileNo, SmsTypeEnum smsType){
		try {
			String smsCode = RandomUtil.randomNumbers(6);
//			String smsCode = "123456";
			String redisCodeValue = smsCode + "|" + System.currentTimeMillis();
			String redisCodeKey = smsType.getName() + mobileNo + "code";
			RedisUtils redisUtils = RedisUtils.getInstance();
			/*//需要加入连接短信服务器发送验证码的方法
			String redisNumKey = smsType.getName() + mobileNo + "num";

			同一手机，两分钟只能发送一次验证码
			Object codeRedis = redisUtils.get(redisCodeKey);
			if(codeRedis != null){
				long time  = Long.valueOf(codeRedis.toString().split("\\|")[1]);
				if((System.currentTimeMillis() - time) / 1000 < 60){
					MarvelsLogUtil.getInstance().info("验证码发送成功【" + mobileNo + "】发送验证码频繁，一分钟只能发送一次");
					return ResponseResult.getFailResult("发送短信验证码太频繁");
				}
			}
			int sendTimesInt=0;
			验证码一个小时内，只能连续发送三次。当验证码验证通过时，将次数置为零，可重新发送
			Object sendTimes = redisUtils.get(redisNumKey);
			if(sendTimes != null){
				sendTimesInt = Integer.valueOf(sendTimes.toString());
				if(sendTimesInt >= 3){
					MarvelsLogUtil.getInstance().info("验证码发送成功【" + mobileNo + "】发送验证码频繁，没有通过验证的情况下，一个小时内只能发送3次");
					return ResponseResult.getFailResult("发送短信验证码太频繁");
				}
			}*/
			MarvelsLogUtil.getInstance().info("【" + mobileNo + "】send_message_code==>>" + mobileNo+",验证码为：" + smsCode);
			String[] params = {smsCode,"15"}; //验证码、有效时间
			sendCodeMes(mobileNo,params,smsType.getTemplateName());
			String timeoutStr = PropertiesUtils.getString("sms.codetimeout");
			if(StringUtils.isBlank(timeoutStr)) {
				//15分钟有效
				timeoutStr = "900";
			}
			redisUtils.setex(redisCodeKey, Integer.parseInt(timeoutStr), redisCodeValue);
//			redisUtils.setex(redisNumKey, 60 * 60, sendTimesInt + 1 + "");
			return ResponseResult.getSuccessResult("发送成功", null);
		} catch (Exception e) {
			MarvelsLogUtil.getInstance().error("发送验证码出现异常", e);
			return ResponseResult.getExceptionResult("发送异常");
		}
	}
	/**
	 *
	* @Title: sendCodeMes
	* @Description:  发送短信
	* @author 李瑶
	* @date 2018年3月26日 下午6:34:04
	 */
	@Async("sendMes")
	private void sendCodeMes(String phone,SmsParameter smsParameter, String templateKey)throws Exception{
		MarvelsLogUtil.getInstance().info("开始发送短信，phone:"+phone+",smsParameter:"+smsParameter);
		SmsTemplateNew contentTemplate = smsTemplateMapper.selectByTemplateCode(templateKey, true);

		if(contentTemplate==null){
			throw new Exception("数据库未查询到发短信的模板");
		}
		//模板替换
		String content = replaceContent(contentTemplate.getContentTemplateContent(),smsParameter,templateKey);
		if(StringUtils.isBlank(content)){
			throw new Exception("枚举未查询到发短信的模板");
		}
		SmsLog smsLog = new SmsLog();
		smsLog.setPhone(phone);
		smsLog.setContent(content);
		smsLog.setSendTime(new Date());
		String isUat = PropertiesUtils.getString("sms.isuat");
		if("isprd".equals(isUat)){
			MicroNetResponse microNetResponse = sendMes(phone, content);
			if ("0".equals(microNetResponse.getState())) {
				MarvelsLogUtil.getInstance().info("开始发送短信，phone:"+phone+",smsParameter:"+smsParameter+",发送成功");
				smsLog.setStatus(true);
			} else {
				MarvelsLogUtil.getInstance().info("开始发送短信，phone:"+phone+",smsParameter:"+smsParameter+",发送失败");
				smsLog.setStatus(false);
			}
		}else{//测试环境,不实际发短信
			smsLog.setStatus(true);
		}

		smsLogMapper.insert(smsLog);
		MarvelsLogUtil.getInstance().info("开始发送短信，phone:"+phone+",smsParameter:"+smsParameter+",发送结束");
	}

	@Async("sendMes")
	private void sendCodeMes(String phone,String[] params, String templateKey)throws Exception{
		MarvelsLogUtil.getInstance().info("开始发送短信，phone:"+phone+",smsParameter:"+JSON.toJSONString(params));
		SmsTemplateNew contentTemplate = smsTemplateMapper.selectByTemplateCode(templateKey, true);

		if(contentTemplate==null){
			throw new Exception("数据库未查询到发短信的模板");
		}
		//模板替换
		SmsLog smsLog = new SmsLog();
		smsLog.setPhone(JSON.toJSONString(phone));
		smsLog.setContent(JSON.toJSONString(params)+contentTemplate.getContentTemplateContent());
		smsLog.setSendTime(new Date());
		String isUat = PropertiesUtils.getString("sms.isuat");
		if("isprd".equals(isUat)){
			MicroNetResponse microNetResponse = sendMsgForTencent(phone,params,contentTemplate.getContentTemplateContent());
			if ("0".equals(microNetResponse.getState())) {
				MarvelsLogUtil.getInstance().info("开始发送短信，phone:"+phone+",smsParameter:"+JSON.toJSONString(params)+",发送成功");
				smsLog.setStatus(true);
			} else {
				MarvelsLogUtil.getInstance().info("开始发送短信，phone:"+phone+",smsParameter:"+JSON.toJSONString(params)+",发送失败");
				smsLog.setStatus(false);
			}
		}else{//测试环境,不实际发短信
			smsLog.setStatus(true);
		}

		smsLogMapper.insert(smsLog);
		MarvelsLogUtil.getInstance().info("开始发送短信，phone:"+phone+",smsParameter:"+JSON.toJSONString(params)+",发送结束");
	}

	private MicroNetResponse sendMsgForTencent(String mobileNo,String[] params,String templateId
			){
		MarvelsLogUtil.getInstance().info("腾讯云短信发送开始，phone:"+mobileNo+",smsParameter:"+JSON.toJSONString(params)+",签名："+smsSign+",appid："+appid+",模板编号："+templateId);
        /* 必要步骤：
         * 实例化一个认证对象，入参需要传入腾讯云账户密钥对 secretId 和 secretKey
         * 本示例采用从环境变量读取的方式，需要预先在环境变量中设置这两个值
         * 您也可以直接在代码中写入密钥对，但需谨防泄露，不要将代码复制、上传或者分享给他人
         * CAM 密钥查询：https://console.cloud.tencent.com/cam/capi
         */
		SendSmsResponse res = null;
		try {
	        Credential cred = new Credential(secretId, secretKey);
	         // 实例化一个 http 选项，可选，无特殊需求时可以跳过
	        HttpProfile httpProfile = new HttpProfile();
	        // 设置代理
	        //httpProfile.setProxyHost("host");
	        //httpProfile.setProxyPort(port);
	        /* SDK 默认使用 POST 方法。
	         * 如需使用 GET 方法，可以在此处设置，但 GET 方法无法处理较大的请求 */
	        //httpProfile.setReqMethod("POST");
	        /* SDK 有默认的超时时间，非必要请不要进行调整
	         * 如有需要请在代码中查阅以获取最新的默认值 */
	        httpProfile.setConnTimeout(60);
	        /* SDK 会自动指定域名，通常无需指定域名，但访问金融区的服务时必须手动指定域名
	         * 例如 SMS 的上海金融区域名为 sms.ap-shanghai-fsi.tencentcloudapi.com */
	        //httpProfile.setEndpoint("sms.tencentcloudapi.com");
	         /* 非必要步骤:
	         * 实例化一个客户端配置对象，可以指定超时时间等配置 */
	        ClientProfile clientProfile = new ClientProfile();
	        /* SDK 默认用 TC3-HMAC-SHA256 进行签名
	         * 非必要请不要修改该字段 */
	        clientProfile.setSignMethod("HmacSHA256");
	        clientProfile.setHttpProfile(httpProfile);
	        /* 实例化 SMS 的 client 对象
	         * 第二个参数是地域信息，可以直接填写字符串 ap-guangzhou，或者引用预设的常量 */
	        if(StringUtils.isBlank(region)) {
	        	region = Constants.TEN_REGION;
	        }
	        SmsClient client = new SmsClient(cred, region,clientProfile);
	        /* 实例化一个请求对象，根据调用的接口和实际情况，可以进一步设置请求参数
	         * 您可以直接查询 SDK 源码确定接口有哪些属性可以设置
	         * 属性可能是基本类型，也可能引用了另一个数据结构
	         * 推荐使用 IDE 进行开发，可以方便地跳转查阅各个接口和数据结构的文档说明 */
	        SendSmsRequest req = new SendSmsRequest();
	         /* 填充请求参数，这里 request 对象的成员变量即对应接口的入参
	         * 您可以通过官网接口文档或跳转到 request 对象的定义处查看请求参数的定义
	         * 基本类型的设置:
	         * 帮助链接：
	         * 短信控制台：https://console.cloud.tencent.com/smsv2
	         * sms helper：https://cloud.tencent.com/document/product/382/3773 */
	         /* 短信应用 ID: 在 [短信控制台] 添加应用后生成的实际 SDKAppID，例如1400006666 */
	        req.setSmsSdkAppid(appid);
	         /* 短信签名内容: 使用 UTF-8 编码，必须填写已审核通过的签名，可登录 [短信控制台] 查看签名信息 */
	        req.setSign(smsSign);
	         /* 国际/港澳台短信 senderid: 国内短信填空，默认未开通，如需开通请联系 [sms helper] */
	        //String senderid = "xxx";
	        //req.setSenderId(senderid);
	         /* 用户的 session 内容: 可以携带用户侧 ID 等上下文信息，server 会原样返回 */
	        //String session = "xxx";
	        //req.setSessionContext(session);
	         /* 短信码号扩展号: 默认未开通，如需开通请联系 [sms helper] */
	        //String extendcode = "xxx";
	        //req.setExtendCode(extendcode);
	        /* 模板 ID: 必须填写已审核通过的模板 ID，可登录 [短信控制台] 查看模板 ID */
	        //String templateID = "400000";
	        req.setTemplateID(templateId);
	         /* 下发手机号码，采用 e.164 标准，+[国家或地区码][手机号]
	         * 例如+8613711112222， 其中前面有一个+号 ，86为国家码，13711112222为手机号，最多不要超过200个手机号*/
	        String[] mobileNos = {"+86"+mobileNo};
	        req.setPhoneNumberSet(mobileNos);
	         /* 模板参数: 若无模板参数，则设置为空*/
	        req.setTemplateParamSet(params);
	         /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
	         * 返回的 res 是一个 SendSmsResponse 类的实例，与请求对象对应 */
	        res = client.SendSms(req);
	         // 输出 JSON 格式的字符串回包
	        MarvelsLogUtil.getInstance().info("腾讯云短信发送结束，phone:"+mobileNo+",smsParameter:"+JSON.toJSONString(params)+",签名："+smsSign+",appid："+appid+",模板编号："+templateId+"，发送结果返回==>"+JSON.toJSONString(res));
	     } catch (Exception e) {
	    	 MarvelsLogUtil.getInstance().error("腾讯云短信发送失败，phone:"+mobileNo+",smsParameter:"+JSON.toJSONString(params)+",签名："+smsSign+",appid："+appid+",模板编号："+templateId, e);
	     }
		MicroNetResponse microNetResponse = new MicroNetResponse();
		microNetResponse.setState("1");
		if (null != res) {
			microNetResponse.setMsgID(res.getRequestId());
			SendStatus sendStatus = res.getSendStatusSet()[0];
			if(!StringUtils.equalsIgnoreCase(sendStatus.getCode(), "OK")) {
				microNetResponse.setState("-1");
			}
		}

        //记录访问三方接口日志
        try {
        	StateEnum stateEnum = StateEnum.SUCESS;
        	if(StringUtils.equalsIgnoreCase(microNetResponse.getState(), "-1")) {
        		stateEnum = StateEnum.FAIL;
        	}
        	IntDataQueryLog log = new IntDataQueryLog(InterfaceMethodEnum.TEN_SMD_SEND.toString(),microNetResponse.getMsgID(),Calendar.getInstance().getTime(),stateEnum.toString(),JSON.toJSONString(res),"");
        	intDataQueryLogService.insertDataLog(log);
        }catch(Exception ex) {
        	 MarvelsLogUtil.getInstance().error("CardOcrService.cardOcr save log error",ex);
        }

		return microNetResponse;
	}

	/**
	 *
	* @Title: sendMes
	* @Description: 发送短信接口
	* @param phone
	* @param msg
	* @author 李瑶
	* @date 2018年3月26日 下午4:34:58
	 */
	private MicroNetResponse sendMes(String phone,String msg)throws Exception{
		String MICRONET_URL = PropertiesUtils.getString("sms.smsUrl");
		String MICRONET_SNAME = PropertiesUtils.getString("sms.smsName");
		String MICRONET_SPWD = PropertiesUtils.getString("sms.smsPwd");
		String suffix = PropertiesUtils.getString("sms.suffix");
		Map<String, Object> map = new HashMap<String, Object>();
		DataRow returndata = new DataRow();
		String returnResult = "";
		map.put("sname", MICRONET_SNAME);
		map.put("spwd", MICRONET_SPWD);
		map.put("scorpid", "");
		map.put("sprdid", "1012888");
		map.put("sdst", phone);
		try {
			map.put("smsg", URLEncoder.encode(msg + "【" + suffix + "】", "UTF-8"));
		} catch (Exception e1) {
			e1.printStackTrace();
			MarvelsLogUtil.getInstance().info("===========>>>互盟短信请求参数拼装异常");
		}
		String requestParams = ThirdSystemUtil.paramsAssembling(map, "");
		MarvelsLogUtil.getInstance().info("=============>>>>互盟短信请求参数:" + requestParams);
		try {
			returnResult = HttpUtil.SMS(requestParams, MICRONET_URL);
			MarvelsLogUtil.getInstance().info("=============>>>>互盟短信返回参数:"+returnResult);
		} catch (Exception e) {

			MarvelsLogUtil.getInstance().error(">>>>>>>>>>手机号:"+phone+",互盟短信发送异常:",e);
			throw e;
		}
		MicroNetResponse microNetResponse = new MicroNetResponse();
		microNetResponse.setState("1");
		if (StrUtil.isNotEmpty(returnResult)) {
			returndata = ThirdSystemUtil.xmlToDatarow(returnResult);

			microNetResponse.setState(returndata.getString("State"));
			microNetResponse.setMsgID(returndata.getString("MsgID"));
			microNetResponse.setMsgState(returndata.getString("MsgState"));
			microNetResponse.setReserve(returndata.getString("Reserve"));
			//microNetResponse = SystemUtil.xmlToEntity(returnResult, MicroNetResponse.class);//转换方法不兼容异步，所以不用注解转换xml
		}
		//MarvelsLogUtil.getInstance().info("=============>>>>互盟短信返回参数:" + returnResult);
		return microNetResponse;
	}
	/**
	 * 模板替换
	 * @return
	 */
	private String replaceContent(String contentTemplateStr,SmsParameter smsParameter, String templateKey){
		String content = "";
		if(StringUtils.isNoneBlank(contentTemplateStr)){
			content = contentTemplateStr.replace("smsCode", smsParameter.getSmsCode() == null ? "" : smsParameter.getSmsCode())
					.replace("product", smsParameter.getProduct() == null ? "" : smsParameter.getProduct())
					.replace("period", smsParameter.getPeriod() == null ? "" : smsParameter.getPeriod())
					.replace("amount", smsParameter.getAmount() == null ? "" : smsParameter.getAmount())
					.replace("loanDay", smsParameter.getLoanDay() == null ? "" : smsParameter.getLoanDay())
					.replace("repayDay", smsParameter.getRepayDay() == null ? "" : smsParameter.getRepayDay())
					.replace("userName", smsParameter.getUserName() == null ? "" : smsParameter.getUserName())
					.replace("expireDay", smsParameter.getExpireDay() == null ? "" : smsParameter.getExpireDay())
					.replace("overdueDay", smsParameter.getOverdueDay() == null ? "" : smsParameter.getOverdueDay())
					.replace("totalAmount", smsParameter.getTotalAmount() == null ? "" : smsParameter.getTotalAmount())
					.replace("digitsNumber", smsParameter.getDigitsNumber() == null ? "" : smsParameter.getDigitsNumber())
					.replace("bankCard", smsParameter.getBankCard() == null ? "" : smsParameter.getBankCard())
					.replace("bankName", smsParameter.getBankName() == null ? "" : smsParameter.getBankName())
					.replace("bankNum4", smsParameter.getBankCard() == null ? "" : smsParameter.getBankCard());
		}
		return content;
	}

	@Override
	public SmsTemplateNew getSmsTemplateNew(SmsTypeEnum smsTypeEnum) {
		SmsTemplateNew contentTemplate = smsTemplateMapper.selectByTemplateCode(smsTypeEnum.getTemplateName(), true);
		return contentTemplate;
	}
}
